Der Windows Developer hatte bereits in Ausgabe 11.2020 ausführlich über die Previews 1 bis 7 von .NET 5.0 berichtet. Dieser Beitrag behandelt ergänzend dazu die Verbesserungen, die in Preview 8 sowie den beiden Release-Candidate-Versionen und der Endfassung erschienen sind. Eigentlich sollte die Preview-8-Version schon „feature complete“ sein; das hat bei Microsoft allerdings nicht geklappt, denn selbst in Release Candidate 2 wurden noch Neuerungen und Breaking Changes ausgeliefert.
Die Erkenntnisse aus Preview 1 bis 7 seien hier kurz zusammengefasst:
- Technisch ist .NET 5.0 der Nachfolger von .NET Core 3.1. Der Begriff „Core“ entfällt und die Versionsnummer 4.0 wird übersprungen, um zu suggerieren, dass .NET 5.0 auch der Nachfolger von .NET Framework 4.8 ist – was aber nur Marketing ist.
- Die ursprünglich für .NET 5.0 angekündigte Integration von Xamarin in .NET Core findet erst in .NET 6.0 statt.
- .NET 5.0 bringt zahlreiche Leistungsverbesserungen im Just-in-Time-Compiler, im Garbage Collector und vielen Basisklassen.
- .NET 5.0 unterstützt nun auch Windows auf ARM64-Prozessoren.
- Der Target Framework Moniker (TFM) ist „net5.0“, ggf. gefolgt von der Zielplattform, z. B. „net5.0-windows“ (für Windows-Forms- und WPF-Anwendungen). Weitere Zielplattformen wie „-ios“ und „-android“ kommen aber erst mit der Integration von Xamarin in .NET 6.0.
- .NET 5.0 enthält die Version 9.0 von C# mit der neuen Klassenart Record, der verkürzten Schreibweise new() zur Instanziierung, Erweiterungen bei Pattern Matching und Source Generators. Visual Basic .NET wird nicht mehr weiterentwickelt.
- Für Windows Forms gibt es in .NET 5.0 ein neues Steuerelement TaskDialog.
- Native Interoperabilität erlaubt den Aufruf von .NET-5.0-Code aus beliebigen Programmiersprachen.
- Interoperabilität zu WinRT ist nicht mehr Teil der .NET-Laufzeitumgebung, sondern wird durch ein NuGet-Paket angeboten.
Der Rest dieses Beitrags beschäftigt sich mit Neuerungen, die Microsoft seit Preview 7 in .NET 5.0 eingeführt hat. Für .NET 5.0 benötigen Sie mindestens Visual Studio Version 2019 16.8, das zeitgleich mit .NET 5.0 erschienen ist.
Blazor 5.0
Die meisten Neuerungen kurz vor Schluss hat das SPA-Web-Framework ASP.NET Core Blazor erhalten. Seit Preview 7 ist Blazor 5.0 überhaupt erst in .NET 5.0 integriert, seit Preview 8 gibt es Neuerungen. Beide Blazor-Varianten, also Blazor Server und Blazor WebAssembly, tragen nun dieselbe Versionsnummer: 5.0. Blazor-WebAssembly-basierte Anwendungen melden sich in .NET 5.0 nun nicht mehr mit „Mono 6.13.0“, sondern mit „.NET 5.0.0“, wenn man die Eigenschaft System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription abfragt. Das liegt daran, dass Blazor WebAssembly jetzt die Klassenbibliotheken von .NET 5.0 verwendet. Die Runtime ist allerdings immer noch Mono, was man beim Kompilieren sieht: mono_wasm_runtime_ready. Die Angleichung der Klassenbibliotheken bedeutet aber nicht, dass nun alle .NET-Klassen in Blazor WebAssembly funktionieren: Weiterhin wirken die Einschränkungen der Browser-Sandbox und der WebAssembly VM. So sind zum Beispiel Dateisystemzugriffe, Multi-Threading, direkte Datenbankzugriffe und andere Netzwerkprotokolle als HTTP/HTTPS weiterhin nicht erlaubt.
Zur Leistungssteigerung von Blazor WebAssembly hatte Microsoft das Komponentenrendering, die JSON-Serialisierung und die Interoperabilität mit JavaScript in Preview 7 beschleunigt (Abb. 1). Neue Blazor-Funktionen gibt es erst seit .NET 5.0 Preview 8.
Wichtigste Neuerung für Blazor WebAssembly 5.0 ist die in der ersten Version (mit Versionsnummer) schmerzlich vermisste Möglichkeit, Anwendungsteile nachzuladen. Lazy Loading von DLLs aus referenzierten Projekten ist nun möglich, wie Abbildung 2 zeigt. Das neu eingeführte Pre-Rendering bei Blazor WebAssembly lässt die erste Seite schneller erscheinen. Auch das Nachladen von JavaScript-Dateien bei Bedarf wird unterstützt. Zudem gibt es CSS-Isolation: Jede Razor Component kann eine eigene CSS-Datei (Komponentenname.razor.css) besitzen, und die dort enthaltenen Styles gelten nur für diese Komponente. JavaScript-Nachladen und CSS-Isolation funktionieren auch in Blazor Server.
Für beide Blazor-Varianten gibt es neue Steuerelemente: <InputRadio> und <InputRadioGroup> sowie <InputFile> zum Dateiupload. Auch kann man nun den Fokus über ElementRef.FocusAsync() auf ein HTML-Element setzen; bisher brauchte man dazu JavaScript. Mit dem Zusatzpaket Microsoft.AspNetCore.Components.Web.Extensions kann man auch die Inhalte des <head>-Tag (z. B. Title, Link und Meta) beeinflussen. Die Schnittstelle IAsyncDisposable wird unterstützt, neu ist auch die Schnittstelle IComponentActivator. Das Paket Microsoft.AspNetCore.ProtectedBrowserStorage, das es schon seit August 2019 als experimentelles Paket gibt, soll offiziell einsatzreif werden (aber nur für Blazor Server). Blazor 5.0 bietet seit Release Candidate 1 eine Komponente <Virtualize>, die aus einer Objektmenge nur die sichtbaren Elemente rendert. Grundsätzlich kann der Entwickler beim Rendering von Blazor-WebAssembly-basierten Anwendungen nun wählen, ob eine statische HTML-Seite vorab erzeugt werden soll. Microsoft hat die Ausführungsgeschwindigkeit von Blazor WebAssembly insgesamt deutlich verbessert (vgl. Leistungsmessungen im Blogeintrag). Ebenso unterstützt Blazor nun das Browserereignis ontoggle für das Tag <details>. Die Klasse MouseEventArgs hat nun die Zusatzeigenschaften OffsetX und OffsetY.
Weitere Funktionen, die für Blazor 5.0 ursprünglich geplant waren, darunter AoT-Kompilierung, SVG-Unterstützung sowie Pflichtparameter in Komponenten, wurden leider gestrichen. Die AOT-Kompilierung soll in Blazor 6.0 kommen: „For .NET 6, we expect to ship support for ahead-of-time (AoT) compilation to WebAssembly, which should further improve performance.“
Entity Framework Core 5.0
Entity Framework Core hat kurz vor Schluss von Version 5.0 noch zwei seit der ersten Version vermisste und in der Vergangenheit (Version 3.x) schon angekündigte, aber dann verschobene Funktionen implementiert:
- Version 5.0 des OR-Mappers erlaubt seit Preview 8 nun wieder das Table-per-Type-Mapping (TPT), mit dem jede Klasse in einer Vererbungshierarchie eine eigene Datenbanktabelle erhält. Bisher setzte Microsoft auf die Zusammenfassung einer ganzen Hierarchie in einer Tabelle (Table-per-Hierarchy, TPH).
- Seit Release Candidate 1 gibt es wieder eine Abstraktion von N:M-Zwischentabellen. Bisher mussten Entwickler N:M-Beziehungen im Objektmodell wie im Datenbankmodell als zwei einzelne 1:N-Beziehungen modellieren. Nun kehrt die aus dem klassischen Entity Framework bekannte Abstraktion zurück, dass zwei Objekte direkt eine Many-to-Many-Beziehung besitzen dürfen und der OR-Mapper das transparent auf eine Zwischentabelle in der Datenbank abbildet.
Die TPT-Vererbung und die Abstraktion von N:M-Zwischentabellen erleichtern die Migration bestehender Anwendungen vom klassischen Entity Framework 6.x sehr.
Es gibt aber seit Preview 7 noch weitere neue Funktionen in Entity Framework Core 5.0. So ist es nun möglich, dass der OR-Mapper für eine einzige Entitätsklasse beim Lesen der Daten eine Sicht (View), beim Schreiben aber eine Tabelle nutzt. Eine einzelne .NET-Klasse kann jetzt durch Indexer Properties Basis für mehrere verschiedene Entitätstypen sein. Solche Shared Type Entities erlauben es, zur Laufzeit der Anwendung Tabellen aus der Datenbank abzubilden, die es zur Entwicklungszeit noch gar nicht gab. Shared Type Entities sind die Basis für die N:M-Abstraktion. Table-Valued-Functions können jetzt nicht nur wie bisher mit FromSqlRaw() bzw. FromSqlInterpolated() aufgerufen werden, sondern der Entwickler kann sie auch direkt auf .NET-Methoden abbilden. Die in Preview 6 neu eingeführten Split Queries können nicht nur pro Abfrage mit AsSplitQuery(), sondern auch zentral mit UseQuerySplittingBehavior() konfiguriert werden. Die vorher schon mögliche Abbildung von Typen auf beliebige SQL-Befehle (Defining Queries) ist mit ToSqlQuery() mächtiger geworden.
Neu ist auch, dass Tabellen mit ExcludeFromMigrations() aus den Schemamigrationen ausgeschlossen werden können. Schemamigrationen führt der OR-Mapper nun im Standard in Transaktionen aus. Per Kommandozeilenbefehl (dotnet ef migrations list) oder Commandlet (Get-Migration) kann man sich ausstehende Migrationen auflisten lassen. Für Schemamigration in SQLite-Datenbanken ist nun auch ein Sichern des Inhalts und Neuanlegen der Tabelle als Schemamigrationsstrategie möglich. Ebenso hat Microsoft in Entity Framework Core 5.0 noch drei Ereignisse ergänzt, die beim Speichern in der Kontextklasse ausgelöst werden: SavingChanges(), SavedChanges() und SaveChangesFailed(). Einfluss auf den Speichervorgang kann der Entwickler in einem sogenannten SaveChangesInterceptor nehmen. Die Größe der in einem Update-, Insert- oder Delete-Batch zur Datenbank gesendeten Befehle hat Microsoft nun im Standard auf 42 (sic!) gesetzt. Diese Zahl ist nicht willkürlich, sondern ist angeblich das gemessene Optimum. Zur Diagnose des Verhaltens des OR-Mappers kann der Betreiber einer Anwendung nun folgendermaßen Event Counter des OR-Mappers abrufen: dotnet counters monitor Microsoft.EntityFrameworkCore.
Click-Once-Deployment
Click-Once-Deployment ist ein seit Langem etabliertes und in der .NET-Welt sehr beliebtes Verfahren zur Verbreitung von .NET-Anwendungen. Es existiert seit Version 2.0 des klassischen .NET Frameworks. Es erlaubt die einfache Verteilung und automatische Aktualisierung von Windows-Anwendungen über Netzwerklaufwerke und Webserver. Die Installation einer Click-Once-basierten Anwendung ist für jeden Windows-Benutzer ohne Administratorrechte möglich und erfolgt im lokalen Benutzerverzeichnis. Das Click-Once-Deployment war nicht in .NET Core 1.0 bis 3.1 enthalten und nach der Microsoft-Ankündigung des Jahres 2019, dass die Übernahme von Funktionen aus dem klassischen .NET Framework nach .NET Core abgeschlossen sei, galt somit auch das Click-Once-Deployment als ausgestorbene Technik. Im Juli 2020 hatte Microsoft dann aber auf GitHub verkündet, dass das beliebte Verfahren in .NET 5.0 nun doch zurückkehren soll.
Click-Once-Deployment ist seit Oktober 2020 nun nicht nur für .NET 5.0, sondern auch für .NET Core 3.0 und 3.1 verfügbar. Ein Click-Once-Deployment-Paket kann der Entwickler über Visual Studio (ab Version 16.8 Preview 5) über Deploy | Folder | Click-Once (Abb. 3 und 4) oder das Kommandozeilenwerkzeug dotnet-mage für das .NET SDK CLI erstellen (Listing 1).
dotnet tool install -g Microsoft.DotNet.Mage cd t:\NET5WPF\bin\Debug\net5.0-windows\ md files mage.net -al NET5WPF.exe -td files mage.net -new application -t files\NET5WPF.manifest -fd files -v 1.0.0.1 mage.net -new Deployment -install true -pub "NET5WPF Publisher" -v 1.0.0.1 -Appmanifest files\NET5WPF.manifest -t NET5WPF.application
Gegenüber dem originalen Click-Once-Deployment in .NET Framework gibt es allerdings Einschränkungen. So sind Programmaktualisierungen nur beim Anwendungsstart, nicht jedoch beim Beenden oder durch Benutzeraktion im Betrieb möglich.
Support
Bei .NET 5.0 ist zu beachten, dass es sich dabei nicht um eine Long-Term-Support-Version handelt wie bei .NET Core 2.1 und .NET Core 3.1, sondern um eine Current-Version, für die es nur drei Monate nach dem Erscheinen der Folgeversion Support gibt. Microsoft hat bisher kein .NET 5.1 angekündigt, sondern nur für November 2021 ein .NET 6.0. Wenn das so bleibt, gibt es für .NET 5.0 Unterstützung bis zum Februar 2022. Auch .NET Core 3.0 war eine Current-Version; der Support endete schon im März 2020, da .NET Core 3.1 im Dezember 2019 erschien. Inzwischen weist Visual Studio die Entwickler in der Target-Framework-Auswahl eines Projekts auf die Supportrichtlinien hin (Abb. 5). Erst .NET 6.0 soll wieder einen Long-Term-Support mit drei Jahren Unterstützung erhalten. Jede gerade Version in ungeraden Jahren (also dann wieder .NET 8.0 im Jahr 2023) soll eine Long-Term-Support-Version werden. Die zehn Jahre Support, die es für das klassische .NET Framework gab, gibt es in der neuen agilen .NET-Welt leider nicht mehr.
Sonstige Neuerungen
Beim Application Trimming im Rahmen von dotnet publish hat Microsoft in .NET 5.0 den neuen Modus <TrimMode>Link</TrimMode> eingeführt. Im Gegensatz zum bisherigen <TrimMode>copyused</TrimMode> werden nicht nur ungenutzte Assemblies, sondern per Tree Shaking auch ungenutzte Typen und Mitglieder entfernt. Das Trimming schaltet man wie bisher mit <PublishTrimmed>true</PublishTrimmed> ein; copyused ist dann Standard. Das Trimming nimmt einige Zeit in Anspruch.
Zur Problemdiagnose können Entwickler in .NET 5.0 auch Dump-Dateien von .NET-Prozessen auf macOS erstellen und Linux-Dumps in Windows auswerten. Die Formatierung von Ausgaben des ConsoleFormatter ist jetzt anpassbar. Ein neuer JSON Console Logger liefert Ausgaben im JSON-Format. Mit dem neuen .NET-CLI-basierten Kommandozeilenwerkzeug dotnet-runtimeinfo kann man sich Basisinformationen über das Betriebssystem und die aktuelle .NET-Version liefern lassen.
Fazit und Ausblick
Microsoft hat kurz vor Schluss noch einige schöne Funktionen bei .NET 5.0 ergänzt. Aber es sind leider auch einige Punkte nicht fertig geworden und wurden auf .NET 6.0 verschoben. Sehr schmerzhaft ist das Fehlen der Ahead-of-Time-Kompilierung, insbesondere für Blazor WebAssembly. Dort wird also weiterhin .NET Intermediate Language in den Browser geladen und interpretiert. Das bedeutet, dass beim Anwendungsstart mehrere MB in den Browser gehievt werden müssen und die Ausführungsgeschwindigkeit weiterhin nicht an Web-Frameworks wie Angular heranreicht. .NET 5.0 ist nicht der große Wurf, der er ursprünglich sein sollte. .NET 5.0 ist eher ein „.NET Core 4.0“. Aber Microsoft signalisiert mit der Wahl der Versionsnummer 5.0 klar, dass die Ära von .NET Framework und .NET Core damit beendet ist.
Natürlich muss man nicht zwingend migrieren. Das .NET Framework 4.8 will Microsoft weiterhin mit Patches versorgen und „für immer“ mit Windows ausliefern. .NET Core 2.1 wird noch bis zum 21.08.2021, .NET Core 3.1 bis 03.12.2022 unterstützt. Aufgrund der Nähe von .NET Core 3.1 zu .NET 5.0 laufen einige Innovation aus .NET 5.0 auch auf .NET Core 3.1, z. B. C# 9.0 und das portierte Click-Once-Deployment.